﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Linq;
using TfsPolicyPack.CustomRegexPathPolicy.Forms;

// HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\TeamFoundation\SourceControl\Checkin Policies
namespace TfsPolicyPack.CustomRegexPathPolicy
{
    [Serializable]
    public class CustomRegexPathPolicy : PolicyBase
    {
        #region Non Public Members

        [NonSerialized]
        private IPendingCheckin _pendingCheckin;
        private PolicySettings _settings;

        #endregion

        #region Properties

        public override string Description
        {
            get { return "This policy allows you to restrict a set of policies to a set of paths specified by this policy using a include/exclude list containing regular expressions."; }
        }

        public override string Type
        {
            get { return "Custom Regex Path Policy"; }
        }

        public override string TypeDescription
        {
            get { return Description; }
        }

        #endregion

        #region Constructor

        public CustomRegexPathPolicy()
        {
            _settings = new PolicySettings();
        }

        #endregion

        #region Public Methods

        public override bool Edit(IPolicyEditArgs policyEditArgs)
        {
            bool editSuccess = false;
            EditForm editForm = new EditForm();
            editForm.Init(GetCheckinPolicies(policyEditArgs.TeamProject), _settings);

            if (editForm.ShowDialog() == DialogResult.OK)
            {
                _settings = editForm.GetSettings();
                editSuccess = true;
            }

            _settings.TeamProjectName = policyEditArgs.TeamProject.Name;

            return editSuccess;
        }

        public override PolicyFailure[] Evaluate()
        {
            List<PolicyFailure> failureList = new List<PolicyFailure>();

            try
            {
                if (_pendingCheckin == null || _pendingCheckin.PendingChanges == null)
                    return failureList.ToArray();

                if (_pendingCheckin.PendingChanges.CheckedPendingChanges == null || _pendingCheckin.PendingChanges.CheckedPendingChanges.Length == 0)
                    return failureList.ToArray();

                TeamProject teamProject = ((VersionControlServer)_pendingCheckin.GetService(typeof(VersionControlServer))).GetTeamProject(_settings.TeamProjectName);
                IEnumerable<TfsPolicy> policyList = GetCheckinPolicies(teamProject);

                PartialPendingCheckin partialPendingCheckin = new PartialPendingCheckin(_pendingCheckin, _settings);

                PolicyInfo firstInvalidPolicy;
                IEnumerable<IPolicyEvaluation> policyEvaluations = GetPolicyEvaluations(policyList, _settings.AffectedPolicies, out firstInvalidPolicy);

                if (firstInvalidPolicy != null)
                    throw new ApplicationException("The affected policy " + firstInvalidPolicy.UniqueName + " does not exist. Please remove it and choose another one.");

                foreach (IPolicyEvaluation policyEvaluation in policyEvaluations)
                {
                    PolicyFailure[] failureArray;

                    using (policyEvaluation)
                    {
                        policyEvaluation.Initialize(partialPendingCheckin);
                        failureArray = policyEvaluation.Evaluate();
                    }

                    failureList.AddRange(Array.ConvertAll(failureArray, f => new PolicyFailure(f.Message, this)));
                }
            }
            catch (Exception exception)
            {
                failureList.AddRange(new [] { new PolicyFailure(exception.Message, this) });
            }

            return failureList.ToArray();
        }

        public override void Activate(PolicyFailure failure)
        {
            MessageBox.Show(failure.Message);
        }

        public override void Initialize(IPendingCheckin pendingCheckin)
        {
            if (pendingCheckin == null)
                return;

            _pendingCheckin = pendingCheckin;
            _pendingCheckin.PendingChanges.CheckedPendingChangesChanged += OnPendingCheckinChanged;
            _pendingCheckin.WorkItems.CheckedWorkItemsChanged += OnPendingCheckinChanged;
        }

        public override void Dispose()
        {
            if (_pendingCheckin == null)
                return;

            _pendingCheckin.PendingChanges.CheckedPendingChangesChanged -= OnPendingCheckinChanged;
            _pendingCheckin.WorkItems.CheckedWorkItemsChanged -= OnPendingCheckinChanged;
            _pendingCheckin = null;
        }

        #endregion

        #region Non Public Methods

        protected void OnPendingCheckinChanged(object sender, EventArgs e)
        {
            OnPolicyStateChanged(Evaluate());
        }

        private static IEnumerable<IPolicyEvaluation> GetPolicyEvaluations(IEnumerable<TfsPolicy> availablePolicies, IEnumerable<PolicyInfo> selectedPolicies, out PolicyInfo firstInvalidPolicy)
        {
            List<IPolicyEvaluation> result = new List<IPolicyEvaluation>();
            firstInvalidPolicy = null;

            foreach (PolicyInfo selectedPolicy in selectedPolicies)
            {
                bool policyAdded = false;

                foreach (TfsPolicy availablePolicy in availablePolicies)
                {
                    if ((availablePolicy.Type != selectedPolicy.Type) || (availablePolicy.Occurrence != selectedPolicy.Occurrence))
                        continue;

                    IPolicyEvaluation evaluation = availablePolicy.Policy as IPolicyEvaluation;
                    if (evaluation == null)
                        continue;

                    result.Add(evaluation);
                    policyAdded = true;
                }

                if (firstInvalidPolicy == null && !policyAdded)
                    firstInvalidPolicy = selectedPolicy;
            }

            return result;
        }

        private IEnumerable<TfsPolicy> GetCheckinPolicies(TeamProject teamProject)
        {
            PolicyEnvelope[] checkinPolicies = teamProject.GetCheckinPolicies();
            
            List<TfsPolicy> result = new List<TfsPolicy>();
            Dictionary<string, int> uniquePolicyTypes = new Dictionary<string, int>();

            foreach (PolicyEnvelope envelope in checkinPolicies.Where(x => x.Policy.Type != Type))
            {
                int occurrence;

                uniquePolicyTypes.TryGetValue(envelope.Type, out occurrence);
                uniquePolicyTypes[envelope.Type] = ++occurrence;

                result.Add(new TfsPolicy(envelope.Policy, occurrence, envelope.Enabled));
            }

            return result;
        }

        #endregion
    }
}